/*
 * Copyright (C) 2007 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.common.internal;

import com.facebook.infer.annotation.Nullsafe;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

/** Static utility methods pertaining to {@link Set} instances. */
@Nullsafe(Nullsafe.Mode.LOCAL)
public final class Sets {
  private Sets() {}

  /**
   * Creates a <i>mutable</i>, empty {@code HashSet} instance.
   *
   * @return a new, empty {@code HashSet}
   */
  public static <E> HashSet<E> newHashSet() {
    return new HashSet<E>();
  }

  /**
   * Creates a <i>mutable</i> {@code HashSet} instance containing the given elements in unspecified
   * order.
   *
   * @param elements the elements that the set should contain
   * @return a new {@code HashSet} containing those elements (minus duplicates)
   */
  public static <E> HashSet<E> newHashSet(E... elements) {
    HashSet<E> set = newHashSetWithCapacity(elements.length);
    Collections.addAll(set, elements);
    return set;
  }

  /**
   * Creates a {@code HashSet} instance, with a high enough "initial capacity" that it <i>should</i>
   * hold {@code expectedSize} elements without growth. This behavior cannot be broadly guaranteed,
   * but it is observed to be true for OpenJDK 1.6. It also can't be guaranteed that the method
   * isn't inadvertently <i>oversizing</i> the returned set.
   *
   * @param capacity the number of elements you expect to add to the returned set
   * @return a new, empty {@code HashSet} with enough capacity to hold {@code expectedSize} elements
   *     without resizing
   * @throws IllegalArgumentException if {@code expectedSize} is negative
   */
  public static <E> HashSet<E> newHashSetWithCapacity(int capacity) {
    return new HashSet<E>(capacity);
  }

  /**
   * Creates a <i>mutable</i> {@code HashSet} instance containing the given elements in unspecified
   * order.
   *
   * @param elements the elements that the set should contain
   * @return a new {@code HashSet} containing those elements (minus duplicates)
   */
  public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements) {
    return (elements instanceof Collection)
        ? new HashSet<E>((Collection<E>) elements)
        : newHashSet(elements.iterator());
  }

  /**
   * Creates a <i>mutable</i> {@code HashSet} instance containing the given elements in unspecified
   * order.
   *
   * @param elements the elements that the set should contain
   * @return a new {@code HashSet} containing those elements (minus duplicates)
   */
  public static <E> HashSet<E> newHashSet(Iterator<? extends E> elements) {
    HashSet<E> set = newHashSet();
    while (elements.hasNext()) {
      set.add(elements.next());
    }
    return set;
  }

  /**
   * Creates an empty {@code Set} that uses identity to determine equality. It compares object
   * references, instead of calling {@code equals}, to determine whether a provided object matches
   * an element in the set. For example, {@code contains} returns {@code false} when passed an
   * object that equals a set member, but isn't the same instance. This behavior is similar to the
   * way {@code IdentityHashMap} handles key lookups.
   */
  public static <E> Set<E> newIdentityHashSet() {
    return Sets.newSetFromMap(new IdentityHashMap<E, Boolean>());
  }

  /**
   * Returns a set backed by the specified map. The resulting set displays the same ordering,
   * concurrency, and performance characteristics as the backing map. In essence, this factory
   * method provides a {@link Set} implementation corresponding to any {@link Map} implementation.
   * There is no need to use this method on a {@link Map} implementation that already has a
   * corresponding {@link Set} implementation (such as {@link java.util.HashMap} or {@link
   * java.util.TreeMap}).
   *
   * <p>Each method invocation on the set returned by this method results in exactly one method
   * invocation on the backing map or its {@code keySet} view, with one exception. The {@code
   * addAll} method is implemented as a sequence of {@code put} invocations on the backing map.
   *
   * <p>The specified map must be empty at the time this method is invoked, and should not be
   * accessed directly after this method returns. These conditions are ensured if the map is created
   * empty, passed directly to this method, and no reference to the map is retained, as illustrated
   * in the following code fragment:
   *
   * <pre>{@code
   * Set<Object> identityHashSet = Sets.newSetFromMap(
   *     new IdentityHashMap<Object, Boolean>());
   * }</pre>
   *
   * <p>This method has the same behavior as the JDK 6 method {@code Collections.newSetFromMap()}.
   * The returned set is serializable if the backing map is.
   *
   * @param map the backing map
   * @return the set backed by the map
   * @throws IllegalArgumentException if {@code map} is not empty
   */
  public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
    return Collections.newSetFromMap(map);
  }

  /**
   * Creates an empty {@code CopyOnWriteArraySet} instance.
   *
   * <p><b>Note:</b> if you need an immutable empty {@link Set}, use {@link Collections#emptySet}
   * instead.
   *
   * @return a new, empty {@code CopyOnWriteArraySet}
   * @since 12.0
   */
  public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet() {
    return new CopyOnWriteArraySet<E>();
  }

  /**
   * Creates a <i>mutable</i>, empty {@code LinkedHashSet} instance.
   *
   * @return a new, empty {@code LinkedHashSet}
   */
  public static <E> LinkedHashSet<E> newLinkedHashSet() {
    return new LinkedHashSet<E>();
  }
}
